package com.spa.infrastructure.service.wx;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.UUID;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.spa.application.command.massagist.WithdrawCommand;
import com.spa.application.command.wx.WXOrderCommand;
import com.spa.application.command.wx.WXRefundCommand;
import com.spa.application.dto.wx.ResultDTO;
import com.spa.application.dto.wx.WXPayDTO;
import com.spa.application.service.member.MemberOrderApplicationService;
import com.spa.domain.service.RefundDomainService;
import com.spa.infrastructure.service.wx.result.PaySuccessResult;
import com.spa.infrastructure.service.wx.result.RefundSuccessResult;
import com.spa.infrastructure.util.EnvUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.Notification;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.GsonUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;

@Service
@Slf4j
public class WXPayServiceV3 {

    @Autowired
    private EnvUtil envUtil;

    @Autowired
    private TransferBatchService transferBatchService;

    @Autowired
    private JsapiServiceExtension jsapiServiceExtension;

    @Autowired
    private RefundService refundService;

    @Resource
    private MemberOrderApplicationService memberOrderApplicationService;

    @Autowired
    private RefundDomainService refundDomainService0;

    @Autowired
    private RSAAutoCertificateConfig rsaAutoCertificateConfig;

    @Value("${wx.mchId}")
    private String mchId;

    @Value("${domain}")
    private String myDomain;

    private final Gson gson = GsonUtil.getGson();

    /**
     * 支付
     * @param command
     * @return
     */
    public WXPayDTO jsapi(WXOrderCommand command, String appId) {
        PrepayRequest request = getPrepayRequest(command, appId);
        PrepayWithRequestPaymentResponse response = jsapiServiceExtension.prepayWithRequestPayment(request);
        log.info("PrepayWithRequestPaymentResponse : {}", response);
        return WXPayDTO.builder()
                .appId(response.getAppId())
                .paySign(response.getPaySign())
                .nonceStr(response.getNonceStr())
                .packAge(response.getPackageVal())
                .signType(response.getSignType())
                .timeStamp(response.getTimeStamp())
                .build();
    }

    private PrepayRequest getPrepayRequest(WXOrderCommand command, String appId) {
        String notifyUrl = myDomain + Constant.NOTIFY_URL;
        PrepayRequest request = new PrepayRequest();
        //注意区分开小程序还是公众号
        request.setAppid(appId);
        request.setMchid(mchId);
        request.setNotifyUrl(notifyUrl);
        Payer payer = new Payer();
        payer.setOpenid(command.getOpenid());
        request.setPayer(payer);
        Amount amount = new Amount();
        amount.setCurrency("CNY");
        amount.setTotal(command.getTotal());
        request.setAmount(amount);
        request.setDescription("浴悦到家平台服务");
        request.setOutTradeNo(command.getOut_trade_no());
        return request;
    }

    /**
     * 退款
     * @param refundCommand
     * @throws Exception
     */
    public void refund(WXRefundCommand refundCommand) throws Exception {
        String notifyUrl = myDomain + Constant.NOTIFY_URL;
        String out_trade_no = UUID.fastUUID().toString(true);
        CreateRequest request = new CreateRequest();
        AmountReq amount = new AmountReq();
        amount.setRefund(refundCommand.getRefund().longValue());
        amount.setCurrency(refundCommand.getCurrency());
        amount.setTotal(refundCommand.getTotal().longValue());
        request.setAmount(amount);
        request.setNotifyUrl(notifyUrl);
        request.setOutTradeNo(refundCommand.getOut_trade_no());
        request.setOutRefundNo(out_trade_no);
        request.setReason("订单退款");
        request.setTransactionId(refundCommand.getOut_refund_no());
        log.info("WXRefundCommand : {}, CreateRequest : {}", refundCommand, request);
        Refund refund = refundService.create(request);
        log.info("Refund : {}", refund);
    }

    /**
     * 发起商家转账
     * @param command
     */
    public void withdrawV3(WithdrawCommand command) {
        String out_trade_no = UUID.fastUUID().toString(true);
        String outBatchNo = UUID.fastUUID().toString(true);
        InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
        //注意区分开小程序还是公众号
        request.setAppid("");
        request.setOutBatchNo(outBatchNo);
        request.setBatchName(command.getDesc());
        request.setBatchRemark(command.getDesc());
        request.setTotalAmount(Convert.toLong(command.getAmount()));
        request.setTotalNum(1);
        TransferDetailInput transferDetailInput = new TransferDetailInput();
        transferDetailInput.setOpenid(command.getOpenid());
        transferDetailInput.setOutDetailNo(out_trade_no);
        transferDetailInput.setTransferAmount(Convert.toLong(command.getAmount()));
        transferDetailInput.setTransferRemark(command.getDesc());
        request.setTransferDetailList(Lists.newArrayList(transferDetailInput));
        InitiateBatchTransferResponse response = transferBatchService.initiateBatchTransfer(request);
        log.info("withdrawV3 : {}", response);
    }

    /**
     * 支付、退款回调
     */
    public ResultDTO notification(RequestParam requestParam) throws IOException {
        log.info("RequestParam : {}", requestParam);
        ResultDTO resultDTO = ResultDTO.builder().build();
        Notification notification = gson.fromJson(requestParam.getBody(), Notification.class);
        log.info("Notification : {}", notification);
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        try {
            if (notification.getEventType().equals("TRANSACTION.SUCCESS")){
                //支付成功
                PaySuccessResult paySuccessResult = parser.parse(requestParam, PaySuccessResult.class);
                log.info("PaySuccessResult : {}", paySuccessResult);
                try {
                    if(paySuccessResult.getTradeState().equals("SUCCESS")){
                        //订单支付成功
                        memberOrderApplicationService.orderPaySuccess(paySuccessResult);
                    }
                }catch (Exception e){
                    log.error("支付通知接口 业务处理异常 ： ", e);
                }
                resultDTO.setCode("SUCCESS");
                resultDTO.setMessage("成功");
            } else if (notification.getEventType().equals("REFUND.SUCCESS")) {
                //退款成功通知
                log.info("REFUND.SUCCESS");
                RefundSuccessResult refundSuccessResult = parser.parse(requestParam, RefundSuccessResult.class);
                log.info("RefundSuccessResult {}", refundSuccessResult);
                refundDomainService0.add(refundSuccessResult);
            } else if (notification.getEventType().equals("REFUND.ABNORMAL")) {
                //退款异常通知
                log.info("REFUND.ABNORMAL");
            } else if (notification.getEventType().equals("REFUND.CLOSED")) {
                //退款关闭通知
                log.info("REFUND.CLOSED");
            }
        } catch (MalformedMessageException malformedMessageException) {
            log.error("error", malformedMessageException);
            resultDTO.setCode("FAIL");
            resultDTO.setMessage("回调通知参数不正确、解析通知数据失败");
        } catch (ValidationException validationException) {
            log.error("error", validationException);
            resultDTO.setCode("FAIL");
            resultDTO.setMessage("签名验证失败");
        }
        return resultDTO;
    }

}
